/*
 * Real.cpp
 */
#include <cmath>
#include <exception>

#include "Real.h"

Real::Real():
_value(Real::round_or_truncate(0.0))
{
}

Real::Real(const Real &aReal):
_value(Real::round_or_truncate(aReal.value()))
{
}

Real::Real(const double &aDouble):
_value(Real::round_or_truncate(aDouble))
{
}

Real::~Real()
{
	// Nothing to do.
}

Real operator*(const double &aDouble, const Real &aReal)
{
	return Real(aDouble) * aReal;
}

Real operator/(const double &aDouble, const Real &aReal)
{
	return Real(aDouble) / aReal;
}

std::string Real::to_binary() const
{
	double value(this->value());
	unsigned long long mask(0x8000000000000000);
	const unsigned int SIZEOF_DOUBLE_IN_BITS(sizeof(double) * 8);

	std::string result(SIZEOF_DOUBLE_IN_BITS, '\0');

	unsigned long long *pointer((unsigned long long *)&value);

	unsigned long long bits(*pointer);

	for (unsigned int index = SIZEOF_DOUBLE_IN_BITS; index > 0; --index) {
		result[SIZEOF_DOUBLE_IN_BITS - index] = bits & mask ? '1' : '0';
		mask >>= 1;
	}

	return result;
}

std::ostream &operator<<(std::ostream &out, const Real &aReal)
{
	out << aReal.value();

	return out; 
}

double Real::truncate(double aDouble)
{
	if (Real::precision() < Real::MAX_DIGITS) {
		const unsigned int MANTISSA_SIZE(Real::MAX_DIGITS);
		unsigned long long *pointer((unsigned long long *)&aDouble);
		unsigned long long bit_mask(0xFFFFFFFFFFFFFFFF << (MANTISSA_SIZE - Real::digits));

		*pointer &= bit_mask;
	}

	return aDouble;
}

double Real::round(double aDouble)
{
	if (Real::precision() < Real::MAX_DIGITS) {
		const unsigned int MANTISSA_SIZE(Real::MAX_DIGITS);
		unsigned long long *pointer((unsigned long long *)&aDouble);
		unsigned long long bit_mask(0xFFFFFFFFFFFFFFFF << (MANTISSA_SIZE - Real::digits));

		unsigned long long roundMask = 1;
		roundMask = roundMask << (MANTISSA_SIZE - Real::digits -1);
		roundMask = *pointer & roundMask;
		roundMask = roundMask << 1;

		*pointer &= bit_mask;
		*pointer += roundMask;
	}

	return aDouble;
}

unsigned int Real::precision()
{
	return Real::digits;
}

void Real::precision(unsigned int digits)
{
	if (digits > Real::MAX_DIGITS) {
		// TODO: Create or use a more specific exception.
		throw std::exception();
	}

	Real::digits = digits;
}

// Define the class field for the operating mode. Default is truncate.
bool Real::rounding(false);

// Define the class field and initialize it to 52 significant digits.
unsigned int Real::digits(52);

// Define the class constant and initialize it to 52 digits.
const unsigned int Real::MAX_DIGITS(52);
